﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DiscoveryLogic.DataViz.NetworkDiagram.Measures
{
    /// <summary>
    /// Closeness centrality using geodesic distance is the reciprocal of the 
    /// sum of geodesic distances to all other vertices in the graph. the Score is 
    /// normalized against maximum value
    /// 
    /// Another way of thinking about how close an actor A is to all others is to calculate the
    /// proportion of other actors that A can reach in one step, two steps, three steps, etc (or,
    /// alternatively, the proportion of nodes that reach A in n steps). One can then calculate a
    /// single index for each node by summing up the proportion of other nodes reached (for the
    /// first time) at a given distance, appropriately weighted (e.g. 1 for nodes at distance 1, ½
    /// for nodes at distance 2…). These scores can be then normalised dividing by the
    /// maximum value, if this is considered appropriate.
    /// 
    /// Closeness can be regarded as a measure of how long it will take information to 
    /// spread from a given vertex to other reachable vertices in the network
    /// </summary>
    public class ClosenessCentrality : IVerticeMeasure
    {
        #region Implementation of IVerticeMeasure

        /// <summary>
        /// 
        /// </summary>
        /// <param name="g"></param>
        /// <param name="vs"></param>
        /// <returns></returns>
        public double GetVerticeMeasure(Graph g, params Vertice[] vs)
        {
            if(vs==null || vs.Length !=1)
                throw new Exception("Specify one vertice");

            double sumGeodesicDistance = 0;
            Vertice v = vs[0];
            DijkstraDistanceMeasure distanceMeasure=new DijkstraDistanceMeasure();
            distanceMeasure.Init(g,v);
            foreach(Vertice t in g.Vertices.Values)
            {
                if(t.ID==v.ID)
                    continue;
                List<Guid> path=new List<Guid>();
                double distance = distanceMeasure.ShortestPathTo(t, ref path);
                if (distance < double.PositiveInfinity && distance > 0)
                    sumGeodesicDistance += distance;
            }
            if (sumGeodesicDistance > 0)
            {
                return 1 / sumGeodesicDistance;
            }
            else
                return 0;
        }

        #endregion

        #region Implementation of IMeasure

        /// <summary>
        /// 
        /// </summary>
        public MeasureType MeasureType
        {
            get
            {
                return Measures.MeasureType.Vertice;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public string MeasureName
        {
            get
            {
                return "Centrality";
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public string Algorithm
        {
            get
            {
                return "Freeman";
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public string Description
        {
            get
            {
                return
                    @"Closeness centrality using geodesic distance is the reciprocal of the 
    sum of geodesic distances to all other vertices in the graph. the Score is 
    normalized against maximum value";
            }
        }

        #endregion
    }
}
